home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 126-150 / disk_149 / less / src / command.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  12KB  |  729 lines

  1. /*
  2.  * User-level command processor.
  3.  */
  4.  
  5. #include "less.h"
  6. #include "position.h"
  7. #include <setjmp.h>
  8.  
  9. extern jmp_buf main_loop;
  10. extern int erase_char, kill_char;
  11. extern int pr_type;
  12. extern int sigs;
  13. extern int ispipe;
  14. extern int quit_at_eof;
  15. extern int hit_eof;
  16. extern int sc_width, sc_height;
  17. extern int sc_window;
  18. extern char *first_cmd;
  19. extern char *every_first_cmd;
  20. extern char version[];
  21. extern char current_file[];
  22. extern char *editor;
  23.  
  24. #ifdef AMIGA
  25. extern curr_ac, ac;        /* local argc for file names */
  26. extern char **av;
  27. int user_errors = 0;
  28. #endif
  29.  
  30. static char cmdbuf[90];        /* Buffer for holding a multi-char command */
  31. #if SHELL_ESCAPE
  32. static char shellcmd[200];    /* For holding last shell command for "!!" */
  33. #endif
  34. static char *cp;        /* Pointer into cmdbuf */
  35. static int cmd_col;        /* Current column of the multi-char command */
  36. static char mcc;        /* The multi-char command letter (e.g. '/') */
  37. static char last_mcc;        /* The previous mcc */
  38. static int screen_trashed;    /* The screen has been overwritten */
  39.  
  40. /*
  41.  * Reset command buffer (to empty).
  42.  */
  43. cmd_reset()
  44. {
  45.     cp = cmdbuf;
  46. }
  47.  
  48. /*
  49.  * Backspace in command buffer.
  50.  */
  51.     static int
  52. cmd_erase()
  53. {
  54.     if (cp == cmdbuf)
  55.         /*
  56.          * Backspace past beginning of the string:
  57.          * this usually means abort the command.
  58.          */
  59.         return (1);
  60.  
  61.     if (control_char(*--cp))
  62.     {
  63.         /*
  64.          * Erase an extra character, for the carat.
  65.          */
  66.         backspace();
  67.         cmd_col--;
  68.     }
  69.     backspace();
  70.     cmd_col--;
  71.     return (0);
  72. }
  73.  
  74. /*
  75.  * Set up the display to start a new multi-character command.
  76.  */
  77. start_mcc(c)
  78.     int c;
  79. {
  80.     mcc = c;
  81.     lower_left();
  82.     clear_eol();
  83.     putchr(mcc);
  84.     cmd_col = 1;
  85. }
  86.  
  87. /*
  88.  * Process a single character of a multi-character command, such as
  89.  * a number, or the pattern of a search command.
  90.  */
  91.     static int
  92. cmd_char(c)
  93.     int c;
  94. {
  95.     if (c == erase_char)
  96.     {
  97.         if (cmd_erase())
  98.             return (1);
  99.     } else if (c == kill_char)
  100.     {
  101.         /* {{ Could do this faster, but who cares? }} */
  102.         while (cmd_erase() == 0)
  103.             ;
  104.     } else
  105.     {
  106.         /*
  107.          * Append the character to the string,
  108.          * if there is room in the buffer and on the screen.
  109.          */
  110.         if (cp < &cmdbuf[sizeof(cmdbuf)-1] && cmd_col < sc_width-3)
  111.         {
  112.             *cp++ = c;
  113.             if (control_char(c))
  114.             {
  115.                 putchr('^');
  116.                 cmd_col++;
  117.                 c = carat_char(c);
  118.             }
  119.             putchr(c);
  120.             cmd_col++;
  121.         } else
  122.             bell();
  123.     }
  124.     return (0);
  125. }
  126.  
  127. /*
  128.  * Return the number currently in the command buffer.
  129.  */
  130.     static int
  131. cmd_int()
  132. {
  133.     *cp = '\0';
  134.     cp = cmdbuf;
  135.     return (atoi(cmdbuf));
  136. }
  137.  
  138. /*
  139.  * Move the cursor to lower left before executing a command.
  140.  * This looks nicer if the command takes a long time before
  141.  * updating the screen.
  142.  */
  143.     static void
  144. cmd_exec()
  145. {
  146.     lower_left();
  147.     flush();
  148. }
  149.  
  150. /*
  151.  * Display the appropriate prompt.
  152.  */
  153.     static void
  154. prompt()
  155. {
  156.     register char *p;
  157.  
  158.     if (first_cmd != NULL && *first_cmd != '\0')
  159.         /*
  160.          * No prompt necessary if commands are from first_cmd
  161.          * rather than from the user.
  162.          */
  163.         return;
  164.  
  165.     /*
  166.      * If nothing is displayed yet, display starting from line 1.
  167.      */
  168.     if (position(TOP) == NULL_POSITION)
  169.         jump_back(1);
  170.     else if (screen_trashed)
  171.         repaint();
  172.     screen_trashed = 0;
  173.  
  174.     /*
  175.      * Select the proper prompt and display it.
  176.      */
  177.     lower_left();
  178.     clear_eol();
  179.     p = pr_string();
  180.     if (p == NULL)
  181.         putchr(':');
  182.     else
  183.     {
  184.         so_enter();
  185.         putstr(p);
  186.         so_exit();
  187.     }
  188. }
  189.  
  190. /*
  191.  * Get command character.
  192.  * The character normally comes from the keyboard,
  193.  * but may come from the "first_cmd" string.
  194.  */
  195.     static int
  196. getcc()
  197. {
  198.     if (first_cmd == NULL)
  199.         return (getchr());
  200.  
  201.     if (*first_cmd == '\0')
  202.     {
  203.         /*
  204.          * Reached end of first_cmd input.
  205.          */
  206.         first_cmd = NULL;
  207.         if (cp > cmdbuf && position(TOP) == NULL_POSITION)
  208.         {
  209.             /*
  210.              * Command is incomplete, so try to complete it.
  211.              * There are only two cases:
  212.              * 1. We have "/string" but no newline.  Add the \n.
  213.              * 2. We have a number but no command.  Treat as #g.
  214.              * (This is all pretty hokey.)
  215.              */
  216.             if (mcc != ':')
  217.                 /* Not a number; must be search string */
  218.                 return ('\n'); 
  219.             else
  220.                 /* A number; append a 'g' */
  221.                 return ('g');
  222.         }
  223.         return (getchr());
  224.     }
  225.     return (*first_cmd++);
  226. }
  227.  
  228. /*
  229.  * Main command processor.
  230.  * Accept and execute commands until a quit command, then return.
  231.  */
  232.     public void
  233. commands()
  234. {
  235.     register int c;
  236.     register int n;
  237.     register int scroll = 10;
  238.  
  239.     last_mcc = 0;
  240.     setjmp(main_loop);
  241.     mcc = 0;
  242.  
  243.     for (;;)
  244.     {
  245.         /*
  246.          * Display prompt and accept a character.
  247.          */
  248.         psignals();    /* See if any signals need processing */
  249.  
  250.         if (quit_at_eof && (quit_at_eof + hit_eof) > 2)
  251.             /*
  252.              * After hitting end-of-file for the second time,
  253.              * automatically advance to the next file.
  254.              * If there are no more files, quit.
  255.              */
  256.             next_file(1);
  257.  
  258.         cmd_reset();
  259.         prompt();
  260.         c = getcc();
  261.  
  262.     again:
  263.         if (sigs)
  264.             continue;
  265.  
  266.         if (mcc)
  267.         {
  268.             /*
  269.              * We are in a multi-character command.  
  270.              * All chars until newline go into the command buffer.
  271.              * (Note that mcc == ':' is a special case that
  272.              *  means a number is being entered.)
  273.              */
  274.             if (mcc != ':' && (c == '\n' || c == '\r'))
  275.             {
  276.                 char *p;
  277.                 static char fcbuf[100];
  278.  
  279.                 /*
  280.                  * Execute the command.
  281.                  */
  282.                 *cp = '\0';
  283.                 cmd_exec();
  284.                 switch (mcc)
  285.                 {
  286.                 case '/': case '?':
  287.                     search(mcc, cmdbuf, n);
  288.                     break;
  289.                 case '+':
  290.                     for (p = cmdbuf;  *p == '+' || *p == ' ';  p++) ;
  291.                     if (*p == '\0')
  292.                         every_first_cmd = NULL;
  293.                     else
  294.                     {
  295.                         strtcpy(fcbuf, p, sizeof(fcbuf));
  296.                         every_first_cmd = fcbuf;
  297.                     }
  298.                     break;
  299.                 case '-':
  300.                     toggle_option(cmdbuf);
  301.                     break;
  302.                 case 'E':
  303.                     /*
  304.                      * Ignore leading spaces 
  305.                      * in the filename.
  306.                      */
  307.                     for (p = cmdbuf;  *p == ' ';  p++) ;
  308.                     edit(glob(p));
  309.                     break;
  310. #if SHELL_ESCAPE
  311.                 case '!':
  312.                     /*
  313.                      * !! just uses whatever is in shellcmd.
  314.                      * Otherwise, copy cmdbuf to shellcmd,
  315.                      * replacing any '%' with the current
  316.                      * file name.
  317.                      */
  318.                     if (*cmdbuf != '!')
  319.                     {
  320.                         register char *fr, *to;
  321.                         to = shellcmd;
  322.                         for (fr = cmdbuf;
  323.                             *fr != '\0';  fr++)
  324.                         {
  325.                             if (*fr != '%')
  326.                                 *to++ = *fr;
  327.                             else
  328.                             {
  329.                                 strcpy(to,
  330.                                  current_file);
  331.                                 to += strlen(to);
  332.                             }
  333.                         }
  334.                         *to = '\0';
  335.                     }
  336.                     lsystem(shellcmd);
  337.                     screen_trashed = 1;
  338.                     error("!done");
  339.                     break;
  340. #endif
  341.                 }
  342.                 mcc = 0;
  343.             } else
  344.             {
  345.                 if (mcc == ':' && (c < '0' || c > '9') &&
  346.                     c != erase_char && c != kill_char)
  347.                 {
  348.                     /*
  349.                      * This is not part of the number
  350.                      * we were entering.  Process
  351.                      * it as a regular character.
  352.                      */
  353.                     mcc = 0;
  354.                     goto again;
  355.                 }
  356.  
  357.                 /*
  358.                  * Append the char to the command buffer.
  359.                  */
  360.                 if (cmd_char(c))
  361.                 {
  362.                     /* Abort the multi-char command. */
  363.                     mcc = 0;
  364.                     continue;
  365.                 }
  366.                 c = getcc();
  367.                 goto again;
  368.             }
  369.         } else switch (c)
  370.         {
  371.         case '0': case '1': case '2': case '3': case '4':
  372.         case '5': case '6': case '7': case '8': case '9':
  373.             /*
  374.              * First digit of a number.
  375.              */
  376.             start_mcc(':');
  377.             goto again;
  378.  
  379.         case 'f':
  380.         case ' ':
  381.         case CONTROL('F'):
  382.             /*
  383.              * Forward one screen.
  384.              */
  385. #ifdef AMIGA
  386.             if (hit_eof) {
  387.                 if (curr_ac+1 == ac)
  388.                     return;
  389.                 else
  390.                     next_file(1);
  391.             } else
  392. #endif
  393.             {
  394.                 n = cmd_int();
  395.                 if (n <= 0)
  396.                     n = sc_window;
  397.                 cmd_exec();
  398.                 forward(n, 1);
  399.             }
  400.             break;
  401.  
  402.         case 'b':
  403. #ifdef AMIGA
  404.         case 'B':
  405. #endif
  406.         case CONTROL('B'):
  407.             /*
  408.              * Backward one screen.
  409.              */
  410.             n = cmd_int();
  411.             if (n <= 0)
  412.                 n = sc_window;
  413.             cmd_exec();
  414.             backward(n, 1);
  415.             break;
  416.  
  417.         case 'e':
  418.         case 'j':
  419.         case '\r':
  420.         case '\n':
  421.         case CONTROL('E'):
  422.             /*
  423.              * Forward N (default 1) line.
  424.              */
  425.             n = cmd_int();
  426.             if (n <= 0)
  427.                 n = 1;
  428.             cmd_exec();
  429.             forward(n, 0);
  430.             break;
  431.  
  432.         case 'y':
  433.         case 'k':
  434.         case CONTROL('K'):
  435.         case CONTROL('Y'):
  436.             /*
  437.              * Backward N (default 1) line.
  438.              */
  439.             n = cmd_int();
  440.             if (n <= 0)
  441.                 n = 1;
  442.             cmd_exec();
  443.             backward(n, 0);
  444.             break;
  445.  
  446.         case 'd':
  447.         case CONTROL('D'):
  448.             /*
  449.              * Forward N lines 
  450.              * (default same as last 'd' or 'u' command).
  451.              */
  452.             n = cmd_int();
  453.             if (n > 0)
  454.                 scroll = n;
  455.             cmd_exec();
  456.             forward(scroll, 0);
  457.             break;
  458.  
  459.         case 'u':
  460.         case CONTROL('U'):
  461.             /*
  462.              * Forward N lines 
  463.              * (default same as last 'd' or 'u' command).
  464.              */
  465.             n = cmd_int();
  466.             if (n > 0)
  467.                 scroll = n;
  468.             cmd_exec();
  469.             backward(scroll, 0);
  470.             break;
  471.  
  472.         case 'R':
  473.             /*
  474.              * Flush buffers, then repaint screen.
  475.              * Don't flush the buffers on a pipe!
  476.              */
  477.             if (!ispipe)
  478.                 ch_init(0);
  479.             /* Fall thru */
  480.         case 'r':
  481.         case CONTROL('R'):
  482.         case CONTROL('L'):
  483.             /*
  484.              * Repaint screen.
  485.              */
  486.             repaint();
  487.             break;
  488.  
  489.         case 'g':
  490.             /*
  491.              * Go to line N, default beginning of file.
  492.              */
  493.             n = cmd_int();
  494.             if (n <= 0)
  495.                 n = 1;
  496.             cmd_exec();
  497.             jump_back(n);
  498.             break;
  499.  
  500.         case 'p':
  501.         case '%':
  502.             /*
  503.              * Go to a specified percentage into the file.
  504.              */
  505.             n = cmd_int();
  506.             if (n < 0)
  507.                 n = 0;
  508.             if (n > 100)
  509.                 n = 100;
  510.             cmd_exec();
  511.             jump_percent(n);
  512.             break;
  513.  
  514.         case 'G':
  515.             /*
  516.              * Go to line N, default end of file.
  517.              */
  518.             n = cmd_int();
  519.             cmd_exec();
  520.             if (n <= 0)
  521.                 jump_forw();
  522.             else
  523.                 jump_back(n);
  524.             break;
  525.  
  526.         case '=':
  527.         case CONTROL('G'):
  528.             /*
  529.              * Print file name, etc.
  530.              */
  531.             error(eq_message());
  532.             break;
  533.             
  534.         case 'V':
  535.             /*
  536.              * Print version number, without the "@(#)".
  537.              */
  538.             error(version+4);
  539.             break;
  540.  
  541.         case 'q':
  542. #ifdef AMIGA
  543.         case 'Q':
  544. #endif
  545.             /*
  546.              * Exit.
  547.              */
  548.             /*setjmp(main_loop);*/
  549.             quit();
  550.  
  551.         case '/':
  552.         case '?':
  553.             /*
  554.              * Search for a pattern.
  555.              * Accept chars of the pattern until \n.
  556.              */
  557.             n = cmd_int();
  558.             if (n <= 0)
  559.                 n = 1;
  560.             start_mcc(c);
  561.             last_mcc = c;
  562.             c = getcc();
  563.             goto again;
  564.  
  565.         case 'n':
  566.             /*
  567.              * Repeat previous search.
  568.              */
  569.             n = cmd_int();
  570.             if (n <= 0)
  571.                 n = 1;
  572.             start_mcc(last_mcc);
  573.             cmd_exec();
  574.             search(mcc, (char *)NULL, n);
  575.             mcc = 0;
  576.             break;
  577. #ifdef AMIGA
  578.  
  579.         /* I didn't want a separate help file because people might
  580.             not donwload it and then where would we be */
  581.         case 'H': case 'h':
  582.             help();
  583.             repaint();
  584.             break;
  585. #else
  586.         case 'H':
  587.             /*
  588.              * Help.
  589.              */
  590.             lower_left();
  591.             clear_eol();
  592.             putstr("help");
  593.             cmd_exec();
  594.             help();
  595.             screen_trashed = 1;
  596.             break;
  597. #endif
  598.  
  599.         case 'E':
  600.             /*
  601.              * Edit a new file.  Get the filename.
  602.              */
  603.             cmd_reset();
  604.             start_mcc('E');
  605.             putstr("xamine: ");    /* This looks nicer */
  606.             cmd_col += 8;
  607.             c = getcc();
  608.             goto again;
  609.             
  610.         case '!':
  611. #if SHELL_ESCAPE
  612.             /*
  613.              * Shell escape.
  614.              */
  615.             cmd_reset();
  616.             start_mcc('!');
  617.             c = getcc();
  618.             goto again;
  619. #else
  620.             error("Command not available");
  621.             break;
  622. #endif
  623.  
  624.         case 'v':
  625. #if EDITOR
  626.             if (ispipe)
  627.             {
  628.                 error("Cannot edit standard input");
  629.                 break;
  630.             }
  631.             sprintf(cmdbuf, "%s %s", editor, current_file);
  632.             lsystem(cmdbuf);
  633.             ch_init(0);
  634.             screen_trashed = 1;
  635.             break;
  636. #else
  637.             error("Command not available");
  638.             break;
  639. #endif
  640.  
  641.         case 'N':
  642.             /*
  643.              * Examine next file.
  644.              */
  645.             n = cmd_int();
  646.             if (n <= 0)
  647.                 n = 1;
  648.             next_file(n);
  649.             break;
  650.  
  651.         case 'P':
  652.             /*
  653.              * Examine previous file.
  654.              */
  655.             n = cmd_int();
  656.             if (n <= 0)
  657.                 n = 1;
  658.             prev_file(n);
  659.             break;
  660.  
  661.         case '-':
  662.             /*
  663.              * Toggle a flag setting.
  664.              */
  665.             cmd_reset();
  666.             start_mcc('-');
  667.             c = getcc();
  668.             goto again;
  669.  
  670.         case '+':
  671.             cmd_reset();
  672.             start_mcc('+');
  673.             c = getcc();
  674.             goto again;
  675.  
  676.         case 'm':
  677.             /*
  678.              * Set a mark.
  679.              */
  680.             lower_left();
  681.             clear_eol();
  682.             putstr("mark: ");
  683.             c = getcc();
  684.             if (c == erase_char || c == kill_char)
  685.                 break;
  686.             setmark(c);
  687.             break;
  688. #ifdef AMIGA
  689.             case '&': 
  690.            /* 
  691.             * print the file
  692.             */
  693.                 set_up_print(av[curr_ac]);
  694.             break;
  695. #endif
  696.  
  697.         case '\'':
  698.             /*
  699.              * Go to a mark.
  700.              */
  701.             lower_left();
  702.             clear_eol();
  703.             putstr("goto mark: ");
  704.             c = getcc();
  705.             if (c == erase_char || c == kill_char)
  706.                 break;
  707.             gomark(c);
  708.             break;
  709.  
  710.         default:
  711. #ifdef AMIGA
  712.         if (++user_errors > 2) {
  713.            lower_left();
  714.            clear_eol();
  715.            so_enter();
  716.            putc(c);
  717.            puts(" is an Invalid Command, Type H for help, or Q to quit");
  718.            so_exit();
  719.            /* give him some time to read it, and three more trys */
  720.            Delay(3 * 50L);
  721.            user_errors = 0;
  722.         } else
  723. #endif
  724.             bell();
  725.             break;
  726.         }
  727.     }
  728. }
  729.